"""
工具路由
处理各种工具功能的请求
"""
from flask import Blueprint, render_template, request, jsonify, send_file, current_app
from werkzeug.utils import secure_filename
import os
import uuid
from datetime import datetime

from utils import (
    PDFConverter,
    EncodingConverter,
    DocReplacer,
    ExcelToWordExporter
)

tools_bp = Blueprint('tools', __name__)


def parse_replacement_rules(rules_file_path):
    """
    解析替换规则文件
    
    Args:
        rules_file_path: 规则文件路径
    
    Returns:
        list: 替换规则列表，每项为 {'find': '查找内容', 'replace': '替换内容'}
    """
    rules = []
    try:
        # 尝试多种编码读取
        encodings = ['utf-8', 'gbk', 'gb2312', 'utf-8-sig']
        content = None
        
        for enc in encodings:
            try:
                with open(rules_file_path, 'r', encoding=enc) as f:
                    content = f.read()
                break
            except (UnicodeDecodeError, LookupError):
                continue
        
        if content is None:
            raise Exception("无法读取规则文件，请检查文件编码")
        
        # 解析每一行
        for line_num, line in enumerate(content.split('\n'), 1):
            line = line.strip()
            if not line or line.startswith('#'):  # 跳过空行和注释
                continue
            
            # 使用冒号分隔查找和替换内容
            parts = line.split(':', 1)
            if len(parts) >= 2:
                find_text = parts[0].strip()
                replace_text = parts[1].strip()
                
                if find_text:  # 查找内容不能为空
                    rules.append({
                        'find': find_text,
                        'replace': replace_text,
                        'line_num': line_num
                    })
        
        return rules
    except Exception as e:
        raise Exception(f"解析规则文件失败: {str(e)}")


# ==================== Excel导出测试文档工具 ====================
@tools_bp.route('/excel-to-word')
def excel_to_word():
    """Excel导出测试文档页面"""
    return render_template('tools/excel_to_word.html')


@tools_bp.route('/upload_excel', methods=['POST'])
def upload_excel():
    """上传Excel文件并获取信息"""
    try:
        current_app.logger.info("=" * 50)
        current_app.logger.info("Excel导出工具 - 文件上传")
        
        if 'file' not in request.files:
            current_app.logger.warning("上传失败: 未找到文件")
            return jsonify({'success': False, 'message': '未找到文件'}), 400
        
        file = request.files['file']
        if file.filename == '':
            current_app.logger.warning("上传失败: 未选择文件")
            return jsonify({'success': False, 'message': '未选择文件'}), 400
        
        # 获取原始文件名和扩展名
        original_filename = secure_filename(file.filename)
        file_ext = original_filename.rsplit('.', 1)[-1] if '.' in original_filename else 'xlsx'
        file_basename = original_filename.rsplit('.', 1)[0] if '.' in original_filename else original_filename
        
        # 生成唯一文件名：时间戳_UUID前8位.扩展名
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        unique_id = str(uuid.uuid4())[:8]
        unique_filename = f"{timestamp}_{unique_id}.{file_ext}"
        
        # 保存上传的Excel文件（使用唯一文件名）
        excel_path = os.path.join(current_app.config['UPLOAD_FOLDER'], unique_filename)
        file.save(excel_path)
        current_app.logger.info(f"文件已保存: {original_filename} -> {unique_filename}")
        
        # 获取Excel信息
        exporter = ExcelToWordExporter()
        info = exporter.get_excel_info(excel_path)
        
        if info['success']:
            # 返回唯一文件名（用于后续处理）和原始文件名（用于显示）
            info['filename'] = unique_filename
            info['original_filename'] = original_filename
            info['display_name'] = file_basename  # 不带扩展名的显示名称
            current_app.logger.info(f"Excel信息读取成功 - 工作表: {info.get('sheets', [])}")
            return jsonify(info)
        else:
            # 如果读取失败，删除已上传的文件
            current_app.logger.error(f"Excel信息读取失败: {info.get('message')}")
            try:
                os.remove(excel_path)
            except:
                pass
            return jsonify(info), 400
            
    except Exception as e:
        current_app.logger.error(f"Excel上传异常: {str(e)}", exc_info=True)
        return jsonify({
            'success': False,
            'message': f'上传失败: {str(e)}'
        }), 500


@tools_bp.route('/export_excel_to_word', methods=['POST'])
def export_excel_to_word():
    """执行Excel到Word的导出"""
    try:
        data = request.get_json()
        
        # 获取参数
        filename = data.get('filename')  # 唯一文件名（用于读取）
        display_name = data.get('display_name', '')  # 显示名称（用于输出）
        start_row = int(data.get('start_row', 1))
        end_row = int(data.get('end_row', 1))
        start_col = int(data.get('start_col', 1))
        end_col = int(data.get('end_col', 1))
        
        current_app.logger.info("=" * 50)
        current_app.logger.info("Excel导出工具 - 开始导出")
        current_app.logger.info(f"源文件: {filename}, 范围: 行{start_row}-{end_row}, 列{start_col}-{end_col}")
        
        # 文件路径
        excel_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
        template_path = os.path.join(current_app.config['TEMPLATE_FOLDER'], 'test_template.docx')
        
        # 检查Excel文件是否存在
        if not os.path.exists(excel_path):
            current_app.logger.error(f"Excel文件不存在: {excel_path}")
            return jsonify({
                'success': False,
                'message': f'Excel文件不存在，请重新上传'
            }), 400
        
        # 检查模板是否存在
        if not os.path.exists(template_path):
            current_app.logger.error(f"模板文件不存在: {template_path}")
            return jsonify({
                'success': False,
                'message': f'模板文件不存在，请联系管理员在以下路径放置模板: {template_path}'
            }), 400
        
        # 生成输出文件名（使用原始显示名称或唯一文件名）
        base_name = display_name if display_name else filename.rsplit('.', 1)[0]
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        output_filename = f"测试文档_{base_name}_{timestamp}.docx"
        output_path = os.path.join(current_app.config['OUTPUT_FOLDER'], output_filename)
        
        # 执行导出
        exporter = ExcelToWordExporter()
        result = exporter.export_to_word(
            excel_path, output_path,
            start_row, end_row,
            start_col, end_col,
            template_path
        )
        
        if result['success']:
            result['filename'] = output_filename
            current_app.logger.info(f"导出成功: {output_filename}, 共生成 {result.get('total_files', 0)} 个文档")
            return jsonify(result)
        else:
            current_app.logger.error(f"导出失败: {result.get('message')}")
            return jsonify(result), 500
            
    except Exception as e:
        current_app.logger.error(f"Excel导出异常: {str(e)}", exc_info=True)
        return jsonify({
            'success': False,
            'message': f'导出失败: {str(e)}'
        }), 500


# ==================== PDF转Word工具 ====================
@tools_bp.route('/pdf-to-word')
def pdf_to_word():
    """PDF转Word页面"""
    return render_template('tools/pdf_to_word.html')


@tools_bp.route('/pdf-to-word/convert', methods=['POST'])
def convert_pdf_to_word():
    """转换PDF到Word"""
    # TODO: 实现PDF转Word逻辑
    try:
        if 'file' not in request.files:
            return jsonify({'success': False, 'message': '未找到文件'}), 400
        
        file = request.files['file']
        if file.filename == '':
            return jsonify({'success': False, 'message': '未选择文件'}), 400
        
        # 保存上传的PDF文件
        filename = secure_filename(file.filename)
        pdf_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
        file.save(pdf_path)
        
        # 转换
        converter = PDFConverter()
        output_filename = filename.rsplit('.', 1)[0] + '.docx'
        output_path = os.path.join(current_app.config['OUTPUT_FOLDER'], output_filename)
        
        title = request.form.get('title', '')
        dpi = int(request.form.get('dpi', 200))
        
        # success = converter.convert(pdf_path, output_path, title, dpi)
        
        return jsonify({
            'success': True,
            'message': 'PDF转换成功',
            'filename': output_filename
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'转换失败: {str(e)}'
        }), 500


# ==================== 编码转换工具 ====================
@tools_bp.route('/encoding-converter')
def encoding_converter():
    """编码转换工具页面"""
    return render_template('tools/encoding_converter.html')


@tools_bp.route('/encoding-converter/detect', methods=['POST'])
def detect_encoding():
    """检测文件编码"""
    # TODO: 实现编码检测逻辑
    try:
        if 'file' not in request.files:
            return jsonify({'success': False, 'message': '未找到文件'}), 400
        
        file = request.files['file']
        filename = secure_filename(file.filename)
        file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
        file.save(file_path)
        
        converter = EncodingConverter()
        encoding = converter.detect_encoding(file_path)
        
        return jsonify({
            'success': True,
            'encoding': encoding
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'检测失败: {str(e)}'
        }), 500


@tools_bp.route('/encoding-converter/convert', methods=['POST'])
def convert_encoding():
    """转换文件编码"""
    # TODO: 实现编码转换逻辑
    try:
        data = request.get_json()
        filename = data.get('filename')
        source_encoding = data.get('source_encoding')
        target_encoding = data.get('target_encoding', 'utf-8')
        
        input_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
        output_filename = f"converted_{filename}"
        output_path = os.path.join(current_app.config['OUTPUT_FOLDER'], output_filename)
        
        converter = EncodingConverter()
        success = converter.convert_file_encoding(
            input_path, output_path, source_encoding, target_encoding
        )
        
        if success:
            return jsonify({
                'success': True,
                'message': '转换成功',
                'filename': output_filename
            })
        else:
            return jsonify({'success': False, 'message': '转换失败'}), 500
    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'转换失败: {str(e)}'
        }), 500


# ==================== 文档批量替换工具 ====================
@tools_bp.route('/doc-replace')
def doc_replace():
    """文档批量替换页面"""
    return render_template('tools/doc_replace.html')


@tools_bp.route('/doc-replace/upload', methods=['POST'])
def upload_replace_files():
    """上传要进行替换的文件（支持多文件和文件夹，自动去重，自动识别规则文件）"""
    try:
        current_app.logger.info("=" * 50)
        current_app.logger.info("文档批量替换 - 文件上传")
        
        if 'files' not in request.files:
            current_app.logger.warning("上传失败: 未找到文件")
            return jsonify({'success': False, 'message': '未找到文件'}), 400
        
        files = request.files.getlist('files')
        paths = request.form.getlist('paths')  # 获取相对路径列表（文件夹模式）
        
        if not files or files[0].filename == '':
            current_app.logger.warning("上传失败: 未选择文件")
            return jsonify({'success': False, 'message': '未选择文件'}), 400
        
        # 生成唯一的批次ID
        batch_id = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{str(uuid.uuid4())[:8]}"
        batch_folder = os.path.join(current_app.config['UPLOAD_FOLDER'], batch_id)
        os.makedirs(batch_folder, exist_ok=True)
        current_app.logger.info(f"批次ID: {batch_id}")
        
        uploaded_files = []
        file_paths_set = set()  # 用于去重（使用相对路径）
        rules_file_info = None  # 存储规则文件信息
        
        for idx, file in enumerate(files):
            if file.filename == '':
                continue
            
            # 获取相对路径（如果有）
            if idx < len(paths) and paths[idx]:
                # 文件夹模式：使用webkitRelativePath
                relative_path = paths[idx]
            else:
                # 单文件模式：只使用文件名
                relative_path = secure_filename(file.filename)
            
            # 检查是否是规则文件
            filename = os.path.basename(relative_path)
            if filename == "0_过程文档批量替换信息.txt":
                # 保存为规则文件
                rules_file_path = os.path.join(batch_folder, 'replacement_rules.txt')
                file.save(rules_file_path)
                
                # 读取并解析规则文件内容
                try:
                    rules = parse_replacement_rules(rules_file_path)
                    rules_file_info = {
                        'filename': filename,
                        'relative_path': relative_path,
                        'rules_count': len(rules),
                        'rules': rules  # 返回所有规则供前端编辑
                    }
                except Exception as e:
                    rules_file_info = {
                        'filename': filename,
                        'relative_path': relative_path,
                        'error': str(e)
                    }
                continue
            
            # 只处理.docx文件，忽略其他格式
            if not relative_path.lower().endswith('.docx'):
                continue
            
            # 去重：相同相对路径只保留一个
            if relative_path in file_paths_set:
                continue
            
            file_paths_set.add(relative_path)
            
            # 构建完整的保存路径
            file_path = os.path.join(batch_folder, relative_path)
            
            # 创建必要的子目录
            file_dir = os.path.dirname(file_path)
            if file_dir:
                os.makedirs(file_dir, exist_ok=True)
            
            # 保存文件
            file.save(file_path)
            
            uploaded_files.append({
                'filename': os.path.basename(relative_path),
                'relative_path': relative_path,
                'size': os.path.getsize(file_path)
            })
        
        # 处理手动上传的替换规则文件（如果没有在文件夹中找到）
        rules_file_path = os.path.join(batch_folder, 'replacement_rules.txt')
        if not rules_file_info and 'rules_file' in request.files:
            rules_file = request.files['rules_file']
            if rules_file.filename:
                rules_file.save(rules_file_path)
                
                # 读取并解析规则文件内容
                try:
                    rules = parse_replacement_rules(rules_file_path)
                    rules_file_info = {
                        'filename': rules_file.filename,
                        'relative_path': rules_file.filename,
                        'rules_count': len(rules),
                        'rules': rules  # 返回所有规则供前端编辑
                    }
                except Exception as e:
                    rules_file_info = {
                        'filename': rules_file.filename,
                        'relative_path': rules_file.filename,
                        'error': str(e)
                    }
        
        current_app.logger.info(f"上传成功: {len(uploaded_files)} 个文件")
        if rules_file_info:
            if 'error' in rules_file_info:
                current_app.logger.warning(f"规则文件解析失败: {rules_file_info['error']}")
            else:
                current_app.logger.info(f"规则文件已加载: {rules_file_info['filename']}, 共 {rules_file_info['rules_count']} 条规则")
        
        return jsonify({
            'success': True,
            'message': f'成功上传 {len(uploaded_files)} 个文件',
            'batch_id': batch_id,
            'files': uploaded_files,
            'has_rules_file': rules_file_info is not None,
            'rules_file_info': rules_file_info
        })
        
    except Exception as e:
        current_app.logger.error(f"文件上传异常: {str(e)}", exc_info=True)
        return jsonify({
            'success': False,
            'message': f'上传失败: {str(e)}'
        }), 500


@tools_bp.route('/doc-replace/replace', methods=['POST'])
def batch_replace():
    """批量替换文档内容（支持单条替换和批量规则替换）"""
    try:
        data = request.get_json()
        use_regex = data.get('use_regex', False)
        case_sensitive = data.get('case_sensitive', True)
        batch_id = data.get('batch_id')
        files = data.get('files', [])  # 现在是相对路径列表
        replace_mode = data.get('replace_mode', 'single')  # single 或 batch
        
        current_app.logger.info("=" * 50)
        current_app.logger.info("文档批量替换 - 开始替换")
        current_app.logger.info(f"批次ID: {batch_id}, 模式: {replace_mode}, 文件数: {len(files)}")
        current_app.logger.info(f"配置: 正则={use_regex}, 区分大小写={case_sensitive}")
        
        if not batch_id:
            current_app.logger.warning("替换失败: 批次ID不能为空")
            return jsonify({
                'success': False,
                'message': '批次ID不能为空'
            }), 400
        
        if not files:
            current_app.logger.warning("替换失败: 未选择文件")
            return jsonify({
                'success': False,
                'message': '未选择文件'
            }), 400
        
        # 获取批次目录
        batch_upload_folder = os.path.join(current_app.config['UPLOAD_FOLDER'], batch_id)
        batch_output_folder = os.path.join(current_app.config['OUTPUT_FOLDER'], batch_id)
        
        # 准备替换规则列表
        replacement_rules = []
        
        if replace_mode == 'single':
            # 单条替换模式
            find_text = data.get('find_text')
            replace_text = data.get('replace_text')
            
            if not find_text:
                current_app.logger.warning("替换失败: 查找内容不能为空")
                return jsonify({
                    'success': False,
                    'message': '查找内容不能为空'
                }), 400
            
            replacement_rules = [{
                'find': find_text,
                'replace': replace_text,
                'line_num': 1
            }]
            current_app.logger.info(f"单条替换规则: '{find_text}' -> '{replace_text}'")
        else:
            # 批量规则模式
            # 优先使用前端编辑后的自定义规则
            use_custom_rules = data.get('use_custom_rules', False)
            
            if use_custom_rules:
                # 使用前端传来的自定义规则
                custom_rules = data.get('custom_rules', [])
                
                if not custom_rules:
                    return jsonify({
                        'success': False,
                        'message': '自定义规则为空'
                    }), 400
                
                # 验证并格式化自定义规则
                for idx, rule in enumerate(custom_rules):
                    if not rule.get('find'):
                        continue
                    
                    replacement_rules.append({
                        'find': rule.get('find', ''),
                        'replace': rule.get('replace', ''),
                        'line_num': rule.get('line_num', idx + 1)
                    })
                
                if not replacement_rules:
                    current_app.logger.warning("替换失败: 没有有效的替换规则")
                    return jsonify({
                        'success': False,
                        'message': '没有有效的替换规则'
                    }), 400
                
                current_app.logger.info(f"使用自定义规则: {len(replacement_rules)} 条")
            else:
                # 使用规则文件
                rules_file_path = os.path.join(batch_upload_folder, 'replacement_rules.txt')
                
                if not os.path.exists(rules_file_path):
                    current_app.logger.warning(f"替换失败: 未找到规则文件 {rules_file_path}")
                    return jsonify({
                        'success': False,
                        'message': '未找到替换规则文件'
                    }), 400
                
                try:
                    replacement_rules = parse_replacement_rules(rules_file_path)
                    
                    if not replacement_rules:
                        current_app.logger.warning("替换失败: 规则文件为空")
                        return jsonify({
                            'success': False,
                            'message': '替换规则文件为空或格式不正确'
                        }), 400
                    
                    current_app.logger.info(f"使用规则文件: {len(replacement_rules)} 条规则")
                except Exception as e:
                    current_app.logger.error(f"规则文件解析失败: {str(e)}")
                    return jsonify({
                        'success': False,
                        'message': str(e)
                    }), 400
        
        if not os.path.exists(batch_upload_folder):
            return jsonify({
                'success': False,
                'message': f'批次目录不存在，请重新上传文件'
            }), 400
        
        # 创建输出目录
        os.makedirs(batch_output_folder, exist_ok=True)
        
        replacer = DocReplacer()
        results = {
            'total_files': len(files),
            'processed_files': 0,
            'total_replacements': 0,
            'total_rules': len(replacement_rules),
            'details': [],
            'output_files': [],
            'rule_stats': []  # 每条规则的统计信息
        }
        
        for relative_path in files:
            file_path = os.path.join(batch_upload_folder, relative_path)
            
            # 检查文件是否存在
            if not os.path.exists(file_path):
                results['details'].append({
                    'filename': os.path.basename(relative_path),
                    'replacements': 0,
                    'status': 'error',
                    'message': '文件不存在'
                })
                continue
            
            # 生成输出文件路径（保持相同的目录结构）
            output_path = os.path.join(batch_output_folder, relative_path)
            
            # 创建必要的子目录
            output_dir = os.path.dirname(output_path)
            if output_dir:
                os.makedirs(output_dir, exist_ok=True)
            
            # 复制文件到输出目录
            import shutil
            shutil.copy2(file_path, output_path)
            
            # 执行替换（按顺序应用所有规则）
            total_count = 0
            rule_details = []
            
            try:
                filename = os.path.basename(relative_path)
                
                # 只处理.docx文件
                if not filename.endswith('.docx'):
                    results['details'].append({
                        'filename': filename,
                        'replacements': 0,
                        'status': 'error',
                        'message': '不支持的文件格式（仅支持.docx）'
                    })
                    continue
                
                # 对每条规则依次执行替换
                for rule in replacement_rules:
                    count = replacer.replace_in_docx(
                        output_path, rule['find'], rule['replace'], 
                        use_regex, case_sensitive
                    )
                    
                    total_count += count
                    
                    if replace_mode == 'batch':
                        rule_details.append({
                            'rule_num': rule['line_num'],
                            'find': rule['find'],
                            'replace': rule['replace'],
                            'count': count
                        })
                
                results['processed_files'] += 1
                results['total_replacements'] += total_count
                
                detail_item = {
                    'filename': relative_path,  # 显示相对路径
                    'output_filename': relative_path,
                    'replacements': total_count,
                    'status': 'success'
                }
                
                if replace_mode == 'batch' and rule_details:
                    detail_item['rule_details'] = rule_details
                
                results['details'].append(detail_item)
                results['output_files'].append(relative_path)
                
            except Exception as e:
                results['details'].append({
                    'filename': os.path.basename(relative_path),
                    'replacements': 0,
                    'status': 'error',
                    'message': str(e)
                })
        
        current_app.logger.info("=" * 50)
        current_app.logger.info("替换完成!")
        current_app.logger.info(f"总文件数: {results['total_files']}, 成功处理: {results['processed_files']}")
        current_app.logger.info(f"总替换次数: {results['total_replacements']}")
        current_app.logger.info("=" * 50)
        
        return jsonify({
            'success': True,
            'results': results
        })
    except Exception as e:
        current_app.logger.error(f"批量替换异常: {str(e)}", exc_info=True)
        return jsonify({
            'success': False,
            'message': f'替换失败: {str(e)}'
        }), 500


# ==================== 文件下载 ====================
@tools_bp.route('/download/<filename>')
def download_file(filename):
    """下载生成的文件"""
    try:
        file_path = os.path.join(current_app.config['OUTPUT_FOLDER'], filename)
        current_app.logger.info(f"文件下载: {filename}")
        return send_file(file_path, as_attachment=True)
    except Exception as e:
        current_app.logger.error(f"文件下载失败: {filename}, 错误: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'下载失败: {str(e)}'
        }), 404


@tools_bp.route('/download-batch/<batch_id>')
def download_batch(batch_id):
    """批量下载：将整个批次的文件打包成ZIP"""
    try:
        import zipfile
        import tempfile
        
        current_app.logger.info(f"批量下载: 批次ID={batch_id}")
        
        # 获取批次输出目录
        batch_output_folder = os.path.join(current_app.config['OUTPUT_FOLDER'], batch_id)
        
        if not os.path.exists(batch_output_folder):
            current_app.logger.warning(f"批量下载失败: 批次目录不存在 {batch_output_folder}")
            return jsonify({
                'success': False,
                'message': '批次目录不存在'
            }), 404
        
        # 创建临时ZIP文件
        temp_zip = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
        temp_zip_path = temp_zip.name
        temp_zip.close()
        
        try:
            # 创建ZIP文件
            with zipfile.ZipFile(temp_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                # 遍历批次目录中的所有文件
                for root, dirs, files in os.walk(batch_output_folder):
                    for file in files:
                        file_path = os.path.join(root, file)
                        # 计算相对于批次目录的路径
                        arcname = os.path.relpath(file_path, batch_output_folder)
                        zipf.write(file_path, arcname)
            
            # 生成下载文件名
            zip_filename = f"批量替换结果_{batch_id}.zip"
            
            # 统计文件数量
            file_count = sum(len(files) for _, _, files in os.walk(batch_output_folder))
            current_app.logger.info(f"批量下载成功: {zip_filename}, 包含 {file_count} 个文件")
            
            # 发送文件
            return send_file(
                temp_zip_path,
                as_attachment=True,
                download_name=zip_filename,
                mimetype='application/zip'
            )
            
        finally:
            # 清理临时文件（延迟删除，因为文件正在被发送）
            # 注意：这里可能需要后台任务来清理临时文件
            pass
            
    except Exception as e:
        current_app.logger.error(f"批量下载异常: 批次ID={batch_id}, 错误: {str(e)}", exc_info=True)
        return jsonify({
            'success': False,
            'message': f'打包下载失败: {str(e)}'
        }), 500


@tools_bp.route('/download-batch-file/<batch_id>/<path:relative_path>')
def download_batch_file(batch_id, relative_path):
    """下载批次中的单个文件"""
    try:
        # 获取批次输出目录中的文件
        batch_output_folder = os.path.join(current_app.config['OUTPUT_FOLDER'], batch_id)
        file_path = os.path.join(batch_output_folder, relative_path)
        
        # 安全检查：确保文件路径在批次目录内
        if not os.path.abspath(file_path).startswith(os.path.abspath(batch_output_folder)):
            current_app.logger.warning(f"单文件下载失败: 非法路径 {relative_path}")
            return jsonify({
                'success': False,
                'message': '非法的文件路径'
            }), 403
        
        if not os.path.exists(file_path):
            current_app.logger.warning(f"单文件下载失败: 文件不存在 {file_path}")
            return jsonify({
                'success': False,
                'message': '文件不存在'
            }), 404
        
        # 获取文件名（不含路径）
        filename = os.path.basename(relative_path)
        current_app.logger.info(f"单文件下载: 批次ID={batch_id}, 文件={relative_path}")
        
        return send_file(file_path, as_attachment=True, download_name=filename)
        
    except Exception as e:
        current_app.logger.error(f"单文件下载异常: {relative_path}, 错误: {str(e)}", exc_info=True)
        return jsonify({
            'success': False,
            'message': f'下载失败: {str(e)}'
        }), 500
